React状态管理库对比

2020.12.13 星期日 21:35

实际状态管理项目

实际使用了redux,也是得益于其“啰嗦”的设计,把所有reducer 定义于一处,包括定义action。清晰,方便查找/追踪state
没有使用immutable。但是确实做了一些性能优化的处理,包括state及某个属性如何更新等。

没使用dva,因为是一个框架,内置了router等,并不需要,仅是一个简单的状态管理。
redux 简单强大,刚好满足。不需要再去学习新框架dva

mobx 使用简单,灵活,同样的和redux,dva 需要学习新的状态管理概念/文档。用generator语法糖,增加配置成本。
每一个需要监听的state/属性/action,需要用@observable,@action 去处理。也可以不去监听,混合在一起。
虽然装饰器使得语法明了。但是redux 数据流,有明确的规定,必须先定义action(type,payload),然后去操作reducer。

若在业务项目中使用,无聊mobx或dva 都是非常不错了。奈何更需要的是一个状态(集中)管理的简单库。
如果偏业务,redux确实有一些啰嗦。


github 对比

统计于 20201214

star issues fork note
redux 54.9k 33/1802 14.5k
mobx 22.9k 12/1633 1.9k
dva 15.3k 22/2072 3k
recoil 17.2k 161/689 929 统计于20220722

redux

github: https://github.com/reduxjs/redux
redux文档

dva

github: https://github.com/dvajs/dva/blob/master/README_zh-CN.md
dva 文档

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。

MobX

github: https://github.com/mobxjs/mobx

MobX
MobX

MobX 以前叫做 Mobservable

SMTC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 我们定义一个可观测的store
import { observable } from 'mobx';
class AppStore {
@observable number = 0;
changeNumber(){
this.number++;
}
}
export default AppStore;

// 我们再定义观测store的组件
import React, { Component } from 'react';
import { observer } from 'mobx-react';
@observer
class App extends Component {
render() {
const{store} = this.props;
return <div onClick={store.changeNumber.bind(store)}>
{store.number}
</div>
}
};
export default App;

// 最终我们把store注入进去,然后把组件渲染出来
import React from 'react';
import { render } from 'react-dom';
const store = new AppStore();
render(
<App store={store} />
document.getElementById('root')
);

mobx-react

原理

REDUX的好处

通过action,reducer来完成应用的数据流管理,逻辑简单清晰
reducer函数式的设计,让我们代码变得可观测,可回溯
action的设计,特别适用于多数据联动的场景
REDUX的弊端

啰嗦代码太多
reducer无复用性
性能优化太繁琐(当然可以引入immutable来解决,但是是不是又增加了学习成本,应用的复杂度和代码量呢)
app state和store state的划分
在实际开发中,一方面我们受益于redux的好处,但另一方面我们也受制于redux的弊端,其实某种程度制约了我们的开发效率,因此近端时间学习了下mobx,从它这里找到了能提升开发效率的方法,不是说mobx比redux好,而是mobx更适合我们实际的工作内容,而redux更实用与大型项目的开发

MobX

从这张图我们可以看到mobx其实就是mvvm的概念,数据双向绑定,并且能做到字段级的控制,所以用mobx开发react的项目,我们不需要做太多的性能优化,同时它的store是可以复用的,这对我们快速开发项目特别有帮助

至此,mobx-react的工作原理基本梳理清楚了,大致用一下几句话来概括
1.observe组件第一次渲染的时候,会创建Reaction,组件的render处于当前这个Reaction的上下文中,并通过track建立render中用到的observable建立关系
2.当observable属性修改的时候,会触发onInvalidate方法,实际上就是组件的forceupdate,然后触发组件渲染,又回到了第一步
这篇就先到这里告一段落,后面会对action,compute,atom做进一步的分享

compute

运行到代码1处,通过第一节的分析,autorun的Reaction跟fullName建立观测的关系,而通过上面的分析,compute的帅帅的+this.name表达式跟computeValue的Reaction建立了观测关系

当我们运行p.changeName的时候,根据前面的知识,会触发observer的onBecomeStale,不过computeValue的onBecomeStale跟Reaction的onBecomeStale不一样

action

从源码可以看出,核心就是通过startBatch和endBatch完成一个批量的操作,也就是说我们在executeAction无论改变多少次observable的值,最终只会触发一次Reaction的onInvalidate

对比

dva

经朋友推荐开始接触 dva ,从 2.x 版本开始使用,我也基于这个工具开发了一套项目模版,它简化了 redux 的使用,并且在封装了 redux-saga 和 react-router,同时还可以包含 dva-loading 插件获取 loading 状态等。

在 redux 时代,当我需要新增一种跨页面全局数据的时候,我需要去项目的 reducers 目录定义一下这种数据命名和初始值,然后在 constans 目录中为更改这项数据的操作定义一种唯一的操作类型(type),再去 actions 目录定义一些方法,这些方法最后会得到更改后的数据和操作类型(type),最后再回到 reducers 中根据这个操作类型(type)把数据整合到 reducer 中…可以看到,我在编写 redux 这部分代码的时候需要频繁在 actions 、 constants 、 reducers 这几个目录间切换。

而使用 dva 就可以免除这些困扰了,我只需要一个 model 中就可以完成所有操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// app全局性状态管理
import * as appApis from '../services/app'; // 异步请求接口

export default {
namespace: 'app',

state: {
channels: [],
show: true
},

reducers: {
getChannelsAndGamesSuccess(state, { channels, games }) {
return { ...state, channels, games };
},
changeShow(state, { show }) {
return { ...state, show };
}
},

effects: { // 异步
* getChannelsAndGames(_, { call, put }) {
const res = yield call(appApis.getChannelsAndGames);
yield put({
type: 'getChannelsAndGamesSuccess',
channels: res.channels
});
}
},

subscriptions: { // 订阅
setup({dispatch, history}) {
history.listen(location => {
if (location.pathname == '/') {
dispatch({
type: 'getChannelsAndGames'
});
}
});
}
}
};

mobx

既然 dva 这么好用,为什么还要使用 mobx 呢?还不是为了折腾😅,用了才能知道两者的优劣,同样的基于 mobx 我也创建了一个项目模版。

在使用 dva 的时候,但凡遇到异步请求的时候都需要先定义一个 effects ,请求完成后再触发一个 action 去修改数据,于是,强迫症作怪,这两者的命名总是让我感觉难受和啰嗦,你可以看到我都是定义为 getXxx 和 getXxxSuccess。

action 是修改 state 的唯一途径,是的,所有的状态管理库都是这样的,但是 mobx 通过一些工具函数解决了这一问题:

以上是我最喜欢的两种写法,分别借助了 runInAction 和 asyncAction 这两个工具函数,当然,还有其他方法可以参考。

recoil

recoil-动机: https://recoiljs.org/zh-hans/docs/introduction/motivation

动机

出于兼容性和简便性的考虑,相比使用外部的全局状态,使用 React 内置的状态管理能力是个最佳的选择。但是 React 有这样一些局限性:

组件间的状态共享只能通过将 state 提升至它们的公共祖先来实现,但这样做可能导致重新渲染一颗巨大的组件树。
Context 只能存储单一值,无法存储多个各自拥有消费者的值的集合。
以上两种方式都很难将组件树的顶层(state 必须存在的地方)与叶子组件 (使用 state 的地方) 进行代码分割。
我们希望改善上述的问题的同时,不仅能保留 API 以及语义,还能使其的表现尽可能保持 React 的样子。

Recoil 定义了一个有向图 (directed graph),正交同时又天然连结于你的 React 树上。状态的变化从该图的顶点(我们称之为 atom)开始,流经纯函数 (我们称之为 selector) 再传入组件。基于这样的实现:

React组件设计实践总结05 - 状态管理

## 状态管理
## 你不需要状态管理
## 你不需要复杂的状态管理
## Redux
七,可能还有性能问题
Redux 常见问题:性能
redux 中的 state 树太大会不会有性能问题?
我为什么从 Redux 迁移到了 Mobx
Mobx 与 Redux 的性能对比
### 扩展阅读

## Mobx
Mobx 提供了一个类似 Vue 的响应式系统,相对 Redux 来说 Mobx 的架构更容易理解。 拿官方的图来看:
## RxJS
## 其他状态管理方案
## 扩展阅读

knowledge is no pay,reward is kindness
0%